-
Notifications
You must be signed in to change notification settings - Fork 196
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unsupported content type #1723
Unsupported content type #1723
Conversation
A new generated diff is ready to view.
A new doc preview is ready to view. |
...tware/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt
Show resolved
Hide resolved
@@ -667,16 +695,13 @@ private class ServerHttpBoundProtocolTraitImplGenerator( | |||
Attribute.AllowUnusedMut.render(this) | |||
rust("let mut input = #T::default();", inputShape.builderSymbol(symbolProvider)) | |||
val parser = structuredDataParser.serverInputParser(operationShape) | |||
val noInputs = model.expectShape(operationShape.inputShape).expectTrait<SyntheticInputTrait>().originalId == null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move this condition closer to where it's used.
I don't think this condition is quite right though. This condition is true
when the user did not model any operation input structure. However, I think we want to assert that the body is empty when all of the operation input is bound to other parts of the HTTP message too. For example, with an operation input modeled like this:
structure MyOperationInput {
@httpHeader("header")
header: String
}
We also want to assert that the HTTP body is empty.
Add a protocol test to ensure we don't regress on this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, this condition is wrong in that SyntheticInputTrait
is only added when no operation input/output is modeled for an operation, but it is not if the operation input/output is modeled but is empty. See the documentation in OperationNormalizer.kt
.
For example, consider the Healthcheck
operation of the simple
model:
@readonly
@http(uri: "/healthcheck", method: "GET")
@documentation("Read-only healthcheck operation")
operation Healthcheck {
input: HealthcheckInputRequest,
output: HealthcheckOutputResponse
}
@documentation("Service healthcheck output structure")
structure HealthcheckInputRequest {
}
@documentation("Service healthcheck input structure")
structure HealthcheckOutputResponse {
}
This model will make noInputs
be false
. Only with a:
operation Healthcheck {
}
will noInputs
be true
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is on purpose because of this test. It's the first example you give, but has a non-empty body and a content-type
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see. Interesting. What's the protocol test that asserts that body must be empty when there is no modeled input? I can't seem to find it right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one (which corresponds to the second example you give)
...tware/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt
Show resolved
Hide resolved
...tware/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt
Outdated
Show resolved
Hide resolved
let found_mime = req | ||
.headers() | ||
.ok_or(MissingContentTypeReason::HeadersTakenByAnotherExtractor)? | ||
if req.headers().is_none() || (protocol != Protocol::RestJson1 && protocol != Protocol::RestXml) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we returning Ok(())
for protocols other than these two?
Is the plan to handle e.g. application/x-amz-json-1.0
for the AwsJson 1.0 protocol in a separate PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it's just that a client should always send an empty JSON object payload in AwsJson 1.0, so with {}
in the body this would fail (this only checks whether the body is empty)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see. Good callout. I think it's best then that we don't call this function in the codegen when we're generating AwsJson 1.x. Consider that we want to support custom protocols loaded from decorators, and that we would be performing this check for them inadvertently.
Since this check is now a protocol-specific concern, whether we perform or not this check should be up to the particular protocol implementation. So I would suggest that we add a method to the Protocol.kt
interface that determines whether we should perform this check, and only call the function in the runtime when the protocol implementation of the interface returns true
.
@@ -667,16 +695,13 @@ private class ServerHttpBoundProtocolTraitImplGenerator( | |||
Attribute.AllowUnusedMut.render(this) | |||
rust("let mut input = #T::default();", inputShape.builderSymbol(symbolProvider)) | |||
val parser = structuredDataParser.serverInputParser(operationShape) | |||
val noInputs = model.expectShape(operationShape.inputShape).expectTrait<SyntheticInputTrait>().originalId == null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, this condition is wrong in that SyntheticInputTrait
is only added when no operation input/output is modeled for an operation, but it is not if the operation input/output is modeled but is empty. See the documentation in OperationNormalizer.kt
.
For example, consider the Healthcheck
operation of the simple
model:
@readonly
@http(uri: "/healthcheck", method: "GET")
@documentation("Read-only healthcheck operation")
operation Healthcheck {
input: HealthcheckInputRequest,
output: HealthcheckOutputResponse
}
@documentation("Service healthcheck output structure")
structure HealthcheckInputRequest {
}
@documentation("Service healthcheck input structure")
structure HealthcheckOutputResponse {
}
This model will make noInputs
be false
. Only with a:
operation Healthcheck {
}
will noInputs
be true
.
...tware/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt
Outdated
Show resolved
Hide resolved
...tware/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt
Outdated
Show resolved
Hide resolved
let found_mime = req | ||
.headers() | ||
.ok_or(MissingContentTypeReason::HeadersTakenByAnotherExtractor)? | ||
if req.headers().is_none() || (protocol != Protocol::RestJson1 && protocol != Protocol::RestXml) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see. Good callout. I think it's best then that we don't call this function in the codegen when we're generating AwsJson 1.x. Consider that we want to support custom protocols loaded from decorators, and that we would be performing this check for them inadvertently.
Since this check is now a protocol-specific concern, whether we perform or not this check should be up to the particular protocol implementation. So I would suggest that we add a method to the Protocol.kt
interface that determines whether we should perform this check, and only call the function in the runtime when the protocol implementation of the interface returns true
.
* when there is no modeled body input, content type must not be set and the body must be empty | ||
* Returns a writable to perform this check | ||
*/ | ||
fun serverContentTypeCheckNoModeledInput(): Boolean = false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't want to block approval on this, but now that the prerequisite work has been done in #1698, it would be better to have this in the ServerProtocol
implementations instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After this is merged I'll move this method in #1731
codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/Protocol.kt
Outdated
Show resolved
Hide resolved
...tware/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt
Outdated
Show resolved
Hide resolved
Add validation for the Content-Type header and pass (remove from the failing list) the relevant protocol tests Signed-off-by: Daniele Ahmed <[email protected]>
c0dbea0
to
fc71c5b
Compare
A new generated diff is ready to view.
A new doc preview is ready to view. |
Motivation and Context
Add validation for the Content-Type header and pass (remove from the failing list) the relevant protocol tests.
Closes: #1663
Testing
./gradlew :codegen-server-test:test && ./gradlew :codegen-test:test
Checklist
CHANGELOG.next.toml
if I made changes to the smithy-rs codegen or runtime cratesCHANGELOG.next.toml
if I made changes to the AWS SDK, generated SDK code, or SDK runtime cratesBy submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.